#include"Tetris.h"

#include<random>

void Tetris::initGame() {
    
    m_TetrisBody = new TetrisBody();
    
    initBlock();
}
bool Tetris::initBlock() {
    std::random_device rd;
    std::mt19937 generator(rd());

    if (NextGroupStyle == -1) {
        NextGroupStyle = generator() * generator() % 7;
        m_TetrisBody->GameNextBlock->createBlocks(NextGroupStyle);
    }
    m_TetrisBody->GameMap->createBlocks(NextGroupStyle);

    NextGroupStyle = generator() * generator() % 7;
    m_TetrisBody->GameNextBlock->createBlocks(NextGroupStyle);

    return 0;
}

bool Tetris::CheckBlock() {
    TetrisMap* curMap = m_TetrisBody->GameMap;
    TetrisValue* curValue = m_TetrisBody->GameValue;

    static int estLineCounter = 0;
    estLineCounter = 0;
    
    for (int i = TETRIS_ROWS - 1; i >= 0; i--) {
        while (true) {
            bool flag = true;
            for (int j = 0; j < TETRIS_COLS; j++) {
                if (curMap->StateMap[i][j] == NO_BLOCK) {
                    flag = false;
                    break;
                }
            }
            if (flag) {
                estLineCounter++;

                for (int k = i; k >= 0; k--) {
                    for (int l = 0; l < TETRIS_COLS; l++) {
                        if (k > 0) {
                            curMap->StateMap[k][l] = curMap->StateMap[k - 1][l];
                            if ((curMap->BlockMap[k - 1][l] != NULL) && (curMap->BlockMap[k][l] == NULL)) {
                                int BlockX = curMap->m_x + l * CELL_SIZE;
                                int BlockY = curMap->m_y + k * CELL_SIZE;
                                int BlockColor = curMap->BlockMap[k - 1][l]->m_color;
                                curMap->BlockMap[k][l] = new Block(BlockX, BlockY, BlockColor);
                            }
                            else if ((curMap->BlockMap[k - 1][l] == NULL) && (curMap->BlockMap[k][l] != NULL)) {
                                delete curMap->BlockMap[k][l];
                                curMap->BlockMap[k][l] = NULL;
                            }
                        }
                        else {
                            curMap->StateMap[k][l] = NO_BLOCK;
                            if (curMap->BlockMap[k][l] != NULL) {
                                delete curMap->BlockMap[k][l];
                                curMap->BlockMap[k][l] = NULL;
                            }
                        }
                    }
                }
            }
            else {
                break;
            }
        }

    }
    switch (estLineCounter) {
    case 0: {
        break;
    }
    case 1: {
        curValue->m_score += 10;
        break;
    }
    case 2: {
        curValue->m_score += 30;
        break;
    }
    case 3: {
        curValue->m_score += 50;
        break;
    }
    case 4: {
        curValue->m_score += 70;
        break;
    }
    default: {
        ;
    }
    }

    curValue->m_speed = curValue->m_score / 100 + 1;
    curValue->printMap();
    return 0;
}

void Tetris::exitGame() {
    delete m_TetrisBody;
    m_TetrisBody = NULL;
}

void Tetris::timeout() {
    ExMessage evt;
    while (true) {
        getmessage(&evt);
        if (evt.message == WM_KEYDOWN) {
            switch (evt.vkcode)
            {

            case VK_SPACE: {
                return;
            }
            default:
                break;
            }
        }
    }
}

bool Tetris::Callback(ExMessage evt) {
    if (evt.message == WM_KEYDOWN) {
        switch (evt.vkcode) {
        case VK_LEFT: {
            moveLeft();
            break;
        }
        case VK_RIGHT: {
            moveRight();
            break;
        }
        case VK_DOWN: {
            if (moveDown()) {
                system("pause");
                return true;
            }
            break;
        }
        case VK_UP: {
            rotate();
            break;
        }
        case VK_SPACE: {
            timeout();
            break;
        }
        default: {
            ;
        }
        }
    }

    if (moveDown(1)) {
        system("pause");
        return true;
    }

    return false;
}
bool Tetris::moveDown(int natual) {

    TetrisMap* curMap = m_TetrisBody->GameMap;
    TetrisValue* curValue = m_TetrisBody->GameValue;

    ULONGLONG TickCounter = GetTickCount64();
    if (natual && TickCounter - NatualTickCount < 2000 / sqrt(curValue->m_speed)) {
        return 0;
    }
    else {
        NatualTickCount = TickCounter;
    }

    bool flag = true;
    curMap->FallingBlocks;
    for (int i = 0; i < 4; i++) {
        if (curMap->FallingBlocks[i].second >= TETRIS_ROWS - 1) {
            flag = false;
            break;
        }
        if (curMap->StateMap[curMap->FallingBlocks[i].second + 1][curMap->FallingBlocks[i].first] == HAVE_BLOCK) {
            flag = false;
            break;
        }
    }
    if (flag) {
        int curColor = curMap->BlockMap[curMap->FallingBlocks[0].second][curMap->FallingBlocks[0].first]->m_color;
        for (int i = 0; i < 4; i++) {
            delete curMap->BlockMap[curMap->FallingBlocks[i].second][curMap->FallingBlocks[i].first];
            curMap->BlockMap[curMap->FallingBlocks[i].second][curMap->FallingBlocks[i].first] = NULL;
        }
        for (int i = 0; i < 4; i++) {
            int BlockX = curMap->FallingBlocks[i].first * CELL_SIZE + curMap->m_x;
            int BlockY = (curMap->FallingBlocks[i].second + 1) * CELL_SIZE + curMap->m_y;
            curMap->BlockMap[curMap->FallingBlocks[i].second + 1][curMap->FallingBlocks[i].first] = new Block(BlockX, BlockY, curColor);
        }
        for (int i = 0; i < 4; i++) {
            curMap->FallingBlocks[i].second++;
        }

    }
    else {
        for (int i = 0; i < 4; i++) {
            if (curMap->FallingBlocks[i].second < 2) {
                return 1;
            }
            curMap->StateMap[curMap->FallingBlocks[i].second][curMap->FallingBlocks[i].first] = HAVE_BLOCK;
        }

        CheckBlock();
        initBlock();

    }

    return 0;
}
bool Tetris::moveLeft() {
    bool flag = true;
    for (int i = 0; i < 4; i++) {
        TetrisMap* curMap = m_TetrisBody->GameMap;
        if (curMap->FallingBlocks[i].first <= 0) {
            flag = false;
            break;
        }
        if (curMap->StateMap[curMap->FallingBlocks[i].second][curMap->FallingBlocks[i].first - 1] == HAVE_BLOCK) {
            flag = false;
            break;
        }
    }
    if (flag) {
        TetrisMap* curMap = m_TetrisBody->GameMap;
        int curColor = curMap->BlockMap[curMap->FallingBlocks[0].second][curMap->FallingBlocks[0].first]->m_color;
        for (int i = 0; i < 4; i++) {
            delete curMap->BlockMap[curMap->FallingBlocks[i].second][curMap->FallingBlocks[i].first];
            curMap->BlockMap[curMap->FallingBlocks[i].second][curMap->FallingBlocks[i].first] = NULL;
        }
        for (int i = 0; i < 4; i++) {
            int BlockX = curMap->m_x + (curMap->FallingBlocks[i].first - 1) * CELL_SIZE;
            int BlockY = curMap->m_y + curMap->FallingBlocks[i].second * CELL_SIZE;
            curMap->BlockMap[curMap->FallingBlocks[i].second][curMap->FallingBlocks[i].first - 1] = new Block(BlockX, BlockY, curColor);
        }
        for (int i = 0; i < 4; i++) {
            curMap->FallingBlocks[i].first--;
        }
    }
    return 0;
}
bool Tetris::moveRight() {
    bool flag = true;
    for (int i = 0; i < 4; i++) {
        TetrisMap* curMap = m_TetrisBody->GameMap;
        if (curMap->FallingBlocks[i].first >= TETRIS_COLS - 1) {
            flag = false;
            break;
        }
        if (curMap->StateMap[curMap->FallingBlocks[i].second][curMap->FallingBlocks[i].first + 1] == HAVE_BLOCK) {
            flag = false;
            break;
        }
    }
    if (flag) {
        TetrisMap* curMap = m_TetrisBody->GameMap;
        int curColor = curMap->BlockMap[curMap->FallingBlocks[0].second][curMap->FallingBlocks[0].first]->m_color;
        for (int i = 0; i < 4; i++) {
            delete curMap->BlockMap[curMap->FallingBlocks[i].second][curMap->FallingBlocks[i].first];
            curMap->BlockMap[curMap->FallingBlocks[i].second][curMap->FallingBlocks[i].first] = NULL;
        }
        for (int i = 0; i < 4; i++) {
            int BlockX = curMap->m_x + (curMap->FallingBlocks[i].first + 1) * CELL_SIZE;
            int BlockY = curMap->m_y + curMap->FallingBlocks[i].second * CELL_SIZE;
            curMap->BlockMap[curMap->FallingBlocks[i].second][curMap->FallingBlocks[i].first + 1] = new Block(BlockX, BlockY, curColor);
        }
        for (int i = 0; i < 4; i++) {
            curMap->FallingBlocks[i].first++;
        }
    }
    return 0;
}

bool Tetris::rotate() {
    bool flag = true;
    for (int i = 0; i < 4; i++) {
        TetrisMap* curMap = m_TetrisBody->GameMap;
        int preX = curMap->FallingBlocks[i].first;
        int preY = curMap->FallingBlocks[i].second;
        if (i != 2) {
            int afterX = curMap->FallingBlocks[2].first + preY - curMap->FallingBlocks[2].second;
            int afterY = curMap->FallingBlocks[2].second - preX + curMap->FallingBlocks[2].first;
            if (curMap->StateMap[afterY][afterX] != NO_BLOCK) {
                flag = false;
                break;
            }
            if (afterX < 0 || curMap->StateMap[afterY][afterX] != NO_BLOCK) {
                moveRight();
                return rotate();
            }
            if (afterX > TETRIS_COLS - 1 || curMap->StateMap[afterY][afterX] != NO_BLOCK) {
                moveLeft();
                return rotate();
            }
        }
    }

    if (flag) {
        TetrisMap* curMap = m_TetrisBody->GameMap;
        int curColor = curMap->BlockMap[curMap->FallingBlocks[0].second][curMap->FallingBlocks[0].first]->m_color;
        for (int i = 0; i < 4; i++) {
            delete curMap->BlockMap[curMap->FallingBlocks[i].second][curMap->FallingBlocks[i].first];
            curMap->BlockMap[curMap->FallingBlocks[i].second][curMap->FallingBlocks[i].first] = NULL;
        }
        for (int i = 0; i < 4; i++) {
            int preX = curMap->FallingBlocks[i].first;
            int preY = curMap->FallingBlocks[i].second;
            if (i != 2) {
                curMap->FallingBlocks[i].first = curMap->FallingBlocks[2].first + preY - curMap->FallingBlocks[2].second;
                curMap->FallingBlocks[i].second = curMap->FallingBlocks[2].second - preX + curMap->FallingBlocks[2].first;
            }
        }
        for (int i = 0; i < 4; i++) {
            int BlockX = curMap->m_x + curMap->FallingBlocks[i].first * CELL_SIZE;
            int BlockY = curMap->m_y + curMap->FallingBlocks[i].second * CELL_SIZE;
            curMap->BlockMap[curMap->FallingBlocks[i].second][curMap->FallingBlocks[i].first] = new Block(BlockX, BlockY, curColor);
        }

    }

    return 0;
}
